package com.gitee.srurl.core.api.impl.duplicate;

import cn.hutool.core.util.StrUtil;
import com.gitee.srurl.core.api.IDataStore;
import com.gitee.srurl.core.api.IDuplicateDetection;
import com.gitee.srurl.core.cache.user.dto.ApiUserExtend;
import com.gitee.srurl.core.constant.RedisConstant;
import com.gitee.srurl.core.dto.request.UrlDuplicateCheckResultDTO;
import com.gitee.srurl.core.dto.result.ShortUrlResultDTO;
import com.gitee.srurl.core.exception.ServiceException;
import com.gitee.srurl.core.properties.IdGenProperties;
import com.gitee.srurl.core.properties.id_gen.DoubleDuplicateDetection;
import com.gitee.srurl.core.util.ShortUrlUtil;
import com.gitee.srurl.core.util.SpringContextUtil;
import com.gitee.srurl.core.util.login.LoginHelper;
import com.gitee.srurl.core.util.redis.RedisValueUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author zxw
 * @date 2023/4/30 12:01
 */
@Slf4j
public abstract class CacheDuplicateDetectionTemplate implements IDuplicateDetection {
    private final IDataStore dataStore = SpringContextUtil.getBean(IDataStore.class);
    private final IdGenProperties idGenProperties = SpringContextUtil.getBean(IdGenProperties.class);

    @Override
    public UrlDuplicateCheckResultDTO checkDuplicate(String url) {

        if (StrUtil.isBlank(url)) {
            throw new IllegalArgumentException("待检测url不能为空！");
        }

        // 获取当前用户身份
        ApiUserExtend apiUserExtend = LoginHelper.getApiUserExtend();
        // 非api用户 后台添加的话 是获取不到这个参数的 后台管理员添加的话 使用默认长度
        DoubleDuplicateDetection doubleDuplicateDetection = idGenProperties.getDoubleDuplicateDetection();
        Integer defaultMaxUrlLength = Optional.ofNullable(doubleDuplicateDetection.getDefaultMaxUrlLength()).orElse(-1);
        if (apiUserExtend == null) {
            apiUserExtend = new ApiUserExtend();
            apiUserExtend.setAllowUrlLength(defaultMaxUrlLength);
            apiUserExtend.setCacheRandom("");
        } else {
            Integer allowUrlLength = Optional.ofNullable(apiUserExtend.getAllowUrlLength()).orElse(defaultMaxUrlLength);
            apiUserExtend.setAllowUrlLength(allowUrlLength);
        }

        Integer allowUrlLength = apiUserExtend.getAllowUrlLength();
        if (allowUrlLength > 0) {
            // 需要检测url长度
            if (StrUtil.length(url) > allowUrlLength) {
                throw new ServiceException("待生成url超过账户允许最大长度:{}", allowUrlLength);
            }
        }

        String cacheRandom = Optional.ofNullable(apiUserExtend.getCacheRandom()).orElse("");
        // 缓存查询随机值，生成账号的时候生成
        boolean contains = this.cacheContains(cacheRandom + url);
        // 缓存中不存在 那就是不存在
        if (!contains) {
            return new UrlDuplicateCheckResultDTO(false, null, null, false);
        }
        boolean openDoubleCheck = Optional.ofNullable(doubleDuplicateDetection.getOpen()).orElse(false);
        // 是否开启双重缓存
        ShortUrlResultDTO shortUrlResultDTO = this.deepQueryUrl(url, openDoubleCheck);
        boolean duplicate = false;
        String shortKey = null;
        String shortUrl = null;
        boolean disabled = false;
        if (shortUrlResultDTO != null) {
            // 说明存在
            duplicate = true;
            shortKey = ShortUrlUtil.getShortIdByShortUrl(shortUrlResultDTO.getShortUrl());
            shortUrl = shortUrlResultDTO.getShortUrl();
            disabled = Optional.ofNullable(shortUrlResultDTO.getDisabled()).orElse(true);
            if (openDoubleCheck) {
                // 如果设置双重检测 设置缓存
                RedisValueUtil.setIfAbsent(RedisConstant.Cache.DOUBLE_DUPLICATE_CACHE + url,
                        shortUrlResultDTO, 1, TimeUnit.HOURS);
            }
        }

        return new UrlDuplicateCheckResultDTO(duplicate, shortKey, shortUrl, disabled);
    }

    /**
     * 查询url数据
     *
     * @param url             待检测url
     * @param openDoubleCheck 是否开启双重检测
     * @return 是否已存在
     */
    private ShortUrlResultDTO deepQueryUrl(String url, boolean openDoubleCheck) {

        if (openDoubleCheck) {
            ShortUrlResultDTO shortUrlResultDTO = RedisValueUtil.get(RedisConstant.Cache.DOUBLE_DUPLICATE_CACHE + url);
            if (shortUrlResultDTO == null) {
                return dataStore.queryByOriginUrl(url);
            } else {
                // 说明是存在的
                return shortUrlResultDTO;
            }
        }
        return dataStore.queryByOriginUrl(url);
    }

    /**
     * 缓存中是否包含
     *
     * @param url 待检测url
     * @return 缓存中是否包含该url数据
     */
    protected abstract boolean cacheContains(String url);
}
